package com.lagou.sqlSession;

import com.lagou.config.BoundSql;
import com.lagou.pojo.Configuration;
import com.lagou.pojo.MappedStatement;
import com.lagou.utils.GenericTokenParser;
import com.lagou.utils.ParameterMapping;
import com.lagou.utils.ParameterMappingTokenHandler;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class SimpleExecutor implements Executor{
    @Override
    public <E> E query(Configuration configuration, MappedStatement mappedStatement, Object... args) throws Exception {
        Connection connection = configuration.getDataSource().getConnection();
        String sql = mappedStatement.getSql();
        BoundSql boundSql = getBoundSql(sql);
        // 获取prepareStatement
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());
        String parameterType = mappedStatement.getParameterType();
        Class<?> clazz = getClassType(parameterType);
        // 设置参数,参数不为空的时候才进行设置
        if(args != null){
            List<ParameterMapping> mappingList = boundSql.getParameterMappingList();
            for (int i = 0; i < mappingList.size(); i++) {
                ParameterMapping parameterMapping = mappingList.get(i);
                String content = parameterMapping.getContent();

                // 反射
                // 获取参数类型
                Field field = clazz.getDeclaredField(content);
                field.setAccessible(true);
                Object o = field.get(args[0]);
                preparedStatement.setObject(i+1,o);
            }
        }
        // 执行sql
        ResultSet resultSet = preparedStatement.executeQuery();
        // 封装结果集
        String resultType = mappedStatement.getResultType();
        Class<?> classType = getClassType(resultType);
        ArrayList<Object> objects = new ArrayList<>();
        while (resultSet.next()){
            // 列名
            ResultSetMetaData metaData = resultSet.getMetaData();
            // 生成一个result实体对象
            Object o = classType.newInstance();
            // 查询结果纵列竖
            for (int i = 1; i <= metaData.getColumnCount(); i++) {
                String columnName = metaData.getColumnName(i);

                // 不转writeMethod.invoke(o, object);要报类型错误
                String object = String.valueOf(resultSet.getObject(columnName));

                // 根据表和实体的对应关系，完成实体封装
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, classType);
                Method writeMethod = propertyDescriptor.getWriteMethod();
                writeMethod.invoke(o, object);
            }
            objects.add(o);
        }
        return (E) objects;
    }

    /**
     * 增删改执行这个
     * 增删改不需要返回值类型配置，返回值都是int，表示影响了数据库多少行数据
     * @param configuration
     * @param mappedStatement
     * @param args
     * @return
     */
    @Override
    public int update(Configuration configuration, MappedStatement mappedStatement, Object... args) throws Exception{
        // 获取连接
        Connection connection = configuration.getDataSource().getConnection();
        // 简便操作，设置自动提交
        connection.setAutoCommit(true);
        // 获取mapper中编写的sql
        String sql = mappedStatement.getSql();
        // 获取替换占位符后的sql，以及参数名称
        BoundSql boundSql = getBoundSql(sql);
        // 获取prepareStatement
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());
        // 获取参数类型
        String parameterType = mappedStatement.getParameterType();
        Class<?> clazz = getClassType(parameterType);
        // 设置参数,参数不为空的时候才进行设置
        // 这里传入的参数均为实体对象
        if(args != null){
            List<ParameterMapping> mappingList = boundSql.getParameterMappingList();
            // 循环sql中的占位参数名
            for (int i = 0; i < mappingList.size(); i++) {
                ParameterMapping parameterMapping = mappingList.get(i);
                String content = parameterMapping.getContent();

                // 反射
                // 获取参数类型
                Field field = clazz.getDeclaredField(content);
                field.setAccessible(true);
                // 获取到参数对象中，占位参数名对应的值
                // 此处貌似可以换个方式，调用参数对象的getXXX方法获取
                // 由于传入的参数均为实体对象，所以直接args[0]得到参数对象
                Object o = field.get(args[0]);
                preparedStatement.setObject(i+1,o);
            }
        }
        int i = preparedStatement.executeUpdate();
        return i;
    }

    private Class<?> getClassType(String parameterType) throws ClassNotFoundException {
        if(parameterType != null){
            Class<?> aClass = Class.forName(parameterType);
            return aClass;
        }
        return null;
    }

    private BoundSql getBoundSql(String sql) {
        // 标记处理类，完成对#{}${}的处理
        ParameterMappingTokenHandler tokenHandler = new ParameterMappingTokenHandler();

        // 标记解析器
        GenericTokenParser parser = new GenericTokenParser("#{", "}", tokenHandler);

        String parse = parser.parse(sql);

        // 从动态sql中解析出的属性值，及${}#{}中的属性集合
        List<ParameterMapping> parameterMappings = tokenHandler.getParameterMappings();

        // 封装sql和参数
        BoundSql boundSql = new BoundSql(parse,parameterMappings);
        return boundSql;
    }
}
